package com.demo.workbook.config;

import com.demo.workbook.handler.MyAccessDeniedHandler;
import com.demo.workbook.handler.MyAuthenticationFailureHandler;
import com.demo.workbook.handler.MyAuthenticationSuccessHandler;
import com.demo.workbook.service.impl.UserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;

/**
 * @className: SecurityConfig
 * @copyright: menusifu
 * @description: <一句话功能简述>
 * <功能详细描述>
 * @author: hubin
 * @date: 2020/11/13
 * @version: 1.0
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    UserDetailServiceImpl userDetailService;

    //密码加密
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    //可配置如何通过拦截器保护请求
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭csrf
        http.csrf().disable();

        http.formLogin()//允许用户采用表单登录的方式进行认证
                .usernameParameter("accountName")//自定义用户名入参关键字（UsernamePasswordAuthenticationFilter中默认username）
                .passwordParameter("userpassword")//自定义密码入参（UsernamePasswordAuthenticationFilter中默认password）
                .loginPage("/view/login.html")//设置登录页面
                .loginProcessingUrl("/mylogin")//自定义登录URL,(默认/login)必须和表单提交的actiony一样，POST请求方式。会去执行自定义登录逻辑

                //.successForwardUrl("/main")//登录成功后转发的链接，只支持POST请求//successForwardUrl只是一个转发，无法跳转到外网，例如：百度，所有要使用successHandler（二者互斥只能留一个）
                .successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))
                //successHandler重写入参AuthenticationSuccessHandler，进行重定向
                //登录失败转发的链接，只支持POST请求
                //.failureForwardUrl("/fail")
                //自定义登录失败跳转处理器
                .failureHandler(new MyAuthenticationFailureHandler("http://www.qq.com"));


        http.authorizeRequests()
                //ant表达式
                //?:匹配一个字符
                //*:匹配0个或多个字符
                //**:匹配0个或多个目录
                .antMatchers("/css/**", "/js/**","/**/*.png").permitAll()
                .antMatchers("/view/login.html").permitAll()//不需要认证（登录）,即可访问的资源
                .antMatchers( "/view/fail.html").access("permitAll")//和上面一样，不需要认证，即可访问
                //基于权限的访问
                .antMatchers("/view/res.html").hasAnyAuthority("admin2")//用户需要admin1权限才能访问
                .antMatchers("/view/res2.html").access("@myVerifyPermissionsServiceImpl.hasPermission(request,authentication)")//自定义资源权限校验
                //基于权限的访问
                .antMatchers("/view/res3.html").hasRole("test")//用户test角色权限才能访问

                //基于IP地址的访问
                //.antMatchers("/view/res3.html").hasIpAddress("127.0.0.1")


                .regexMatchers(".+[.]jpg").permitAll()//正则表达式，放行后缀.jpg的资源
                .regexMatchers(HttpMethod.GET,"/demoregex").permitAll()//正则表达式，放行get类型请求的url

                //.mvcMatchers("").servletPath("").permitAll()//主要是可以设置servletPath

                //anyRequest()必须放最后，否则就会报错
                //.anyRequest().denyAll()//全部拒绝
                //.anyRequest().anonymous()//匿名访问,例如购物网站上可以看商品，只有到购买的时候才需要登录
                //.anyRequest().fullyAuthenticated()//完全登录，不可以通过记住密码的方式查看
                //.anyRequest().rememberMe()//记住登录，只需要上次登录过，就可以访问
                .anyRequest().authenticated(); //其他请求必须登录才能访问

        //设置记住我,采用内存方式保存，实际开发中可以记录到数据库重写PersistentTokenRepository接口
        //
        http.rememberMe()
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                //设置前端的入参关键字，和登录userName和password一样，默认值是remember-me
                //.rememberMeParameter("remberMe")
                //超时时间，默认两周，这里面设置600秒
                .tokenValiditySeconds(600)
                //注入自己定义登录逻辑,否则不生效
                .userDetailsService(userDetailService);


        //退出登录相关
        //http.logout()
                //退出登录地url，默认就是/logout
                //.logoutUrl("")
                //退出成功跳转地url
                //.logoutSuccessUrl("");



        //自定义请求拒绝访问异常处理,权限不足情况下处理器.也可以配置ConfigurableServletWebServerFactory相关bean来取代，设置更加的详细
        //二者只能用其一，默认会优先使用http.exceptionHandling().accessDeniedHandler
        //http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler());
    }

    @Bean
    public MyAccessDeniedHandler myAccessDeniedHandler(){
        MyAccessDeniedHandler myAccessDeniedHandler=new MyAccessDeniedHandler();
        return  myAccessDeniedHandler;
    }

    //可配置Spring Security的Filter链
    @Override
    public void configure(WebSecurity web) throws Exception {
        //直接过滤掉该地址，即该地址/vercode不走 Spring Security 过滤器链
        web.ignoring().antMatchers("/vercode","/godo");
    }
}
